package de.mmis.core.base.abstracttree.serializer.internal;

import java.util.Map;

import de.mmis.core.base.DeserializableAnnotations.DeserializationMethod;
import de.mmis.core.base.ObjectDescriptor;
import de.mmis.core.base.abstracttree.InnerNode;
import de.mmis.core.base.abstracttree.LeafNode;
import de.mmis.core.base.abstracttree.Tree;
import de.mmis.core.base.abstracttree.Tree.Type;
import de.mmis.core.base.abstracttree.serializer.DeserializableException;
import de.mmis.core.base.abstracttree.serializer.SerializerHelper;
import de.mmis.core.base.abstracttree.serializer.SerializerHelper.MethodInvocationException;
import de.mmis.core.base.abstracttree.serializer.SerializerHelper.MethodReturnValue;
import de.mmis.core.base.abstracttree.serializer.SerializerHelper.NoMatchingMethodFoundException;
import de.mmis.core.base.abstracttree.serializer.selector.InstanceSelector;
import de.mmis.core.base.abstracttree.serializer.selector.SimpleSelector;

/**
 * Deserializer class for deserializing member method calls from a Trees
 * 
 * @author Christian Koch
 */
public class MemberMethodDeserializer {

	@DeserializationMethod
	public static InstanceSelector deserialize(Tree[] exps,
			ClassLoader classLoader, String id, Map<String, Object> idmap)
			throws DeserializableException {

		if (exps.length < 2)
			throw new DeserializableException(
					new InnerNode(exps),
					"MemberMethodDeserializer needs at least two parameters defining the object and method to be executed");

		Object obj = exps[0].getAs(Object.class, classLoader, idmap);

		ObjectDescriptor objDesc = new ObjectDescriptor(obj, false);

		if (exps[1].getType() != Type.LEAF)
			throw new DeserializableException(new InnerNode(exps),
					"second sub-Tree in struct has to be an Atom indicating the method name");

		String methodname = ((LeafNode) exps[1]).getAsSymbol();

		InnerNode methodExp = new InnerNode(new LeafNode(methodname));
		for (int i = 2; i < exps.length; i++)
			methodExp.add(exps[i]);

		MethodReturnValue mrv;
		try {
			mrv = SerializerHelper.invokeObjectMethod(objDesc, methodExp,
					classLoader, idmap);
		} catch (MethodInvocationException e) {
			throw new DeserializableException(new InnerNode(exps), e,
					"member method call failed");
		} catch (NoMatchingMethodFoundException e) {
			throw new DeserializableException(new InnerNode(exps), e,
					"member method call failed");
		}

		return new SimpleSelector(mrv.getObject(), id, idmap);
	}
}
